# -*- coding: utf-8 -*-
# @Time    : 2017/9/1 22:33
# @Author  : Hochikong
# @Email   : hochikong@foxmail.cm
# @File    : SWTMain.py
# @Software: PyCharm

from swt import app
from flask import render_template, request, json, jsonify, session, abort
from flask_login import LoginManager, login_required, login_user, logout_user, UserMixin
from werkzeug.security import check_password_hash, generate_password_hash
from configparser import ConfigParser
from stockclib.dtSearch import *
import pymongo


# --------------------------------
# Set the constant and read the configuration file
# 设置常量并读取配置文件
CONFIG_FILE = 'config.ini'
DB_SECTION = 'DB'

# --------------------------------
# ConfigParser configuration
# 配置ConfigParser对象
cfg = ConfigParser()
cfg.read(CONFIG_FILE)

# --------------------------------
# MongoDB connection
# 连接MongoDB
connection = pymongo.MongoClient(cfg.get(DB_SECTION, 'address'), int(cfg.get(DB_SECTION, 'port')))
if connection.admin.authenticate(
    cfg.get(DB_SECTION, 'user'),
    cfg.get(DB_SECTION, 'passwd'),
    mechanism='SCRAM-SHA-1',
    source=cfg.get(DB_SECTION, 'database')):
    pass
else:
    raise Exception('Error configure on user or password! ')
database = connection[cfg.get(DB_SECTION, 'database')]
collect_stock_url = database['stock_url']
collect_stocks = database['stocks']
collect_users = database['users']

# --------------------------------
# 数据与模板

html_templates = '<tr class="%s" id="%s"><td>%u</td><td>%s</td><td>%s</td><td>%u</td><td>%u</td>' \
                 '<td>%u</td><td>%u</td><td>%d</td>'\
                 '<td><button type="button" class="btn btn-danger btn-xs" onclick=%s>斩仓</button></td>'
# 分别对应success/danger,stockid,id,股票编号，股票名，持仓，总值，成本，现价，盈余和对应的stockid：\'#stock4\'

admin_table_templates = '<tr class="%s"><td>%s</td><td>%s</td><td><input type=checkbox value=%s></td></tr>'
# 分别是css类、用户名、用户状态和checkbox

# 按钮的模板,注意这里是用了python 3的repr函数才能实现不转义
btn_template = repr("deleteStock(\'#%s\')")

# 写死的持仓数据
datas = {
    'adminroot': [
        'success', 'stock1', 1, '000725', '京东方A', 1000, 3830, 3.81, 3.83, 20], 'userjack': [
            'danger', 'stock1', 1, '000050', '深天马A', 2900, 64467, 22.48, 22.23, -721]}

for d in datas.values():
    d.append(btn_template % d[1])

# 写死的统计汇总数据
statistics = {'adminroot': {
    'money': 100000,
    'totalget': 5000,
    'totallost': 700,
    'totalvalue': 68297,
    'analyzergroup': 'Group2'},
    'userjack': {
    'money': 500,
    'totalget': 200,
    'totallost': 20,
    'totalvalue': 2000,
    'analyzergroup': 'Group3'}
}


# ------------------------------------------
# 登录辅助函数


def query_user(username):  # 返回数据库的用户数据用于登录验证
    # 每次调用validation前需要读取数据库，防止修改了用户属性后还没应用
    user_table = list(collect_users.find())
    for user_info in user_table:
        if user_info['username'] == username:
            return user_info


def validation(data_str):  # 数据库验证
    jsonDict = json.loads(data_str, encoding='utf-8')
    user_name = jsonDict['username']
    passwd = jsonDict['passwd']
    user = query_user(user_name)
    if user is not None and check_password_hash(
            user['passwd_hash'],
            passwd) and user['status'] == 'enable':
        return True
    else:
        return False


def fetch_username(data_str):  # 提取请求数据里的用户名
    jsonDict = json.loads(data_str, encoding='utf-8')
    user_name = jsonDict['username']
    return user_name

# ----------------------------------------------
# flask-login
# user models


class User(UserMixin):
    pass


app.secret_key = 's3cr3t'
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'show_login_page'
login_manager.login_message = 'Unauthorized User'
login_manager.login_message_category = "info"
login_manager.init_app(app)


@login_manager.user_loader
def load_user(username):
    if query_user(username) is not None:
        curr_user = User()
        curr_user.id = username  # 每个用户通过id识别
        return curr_user

# ------------------------------------------------
# 视图函数_页面渲染


@app.route('/')  # 主页
def show_login_page():
    if collect_users.find_one({'username': 'Admin'}):
        return render_template('login.html')
    else:
        return render_template('readyforuse.html')


@app.route('/analyzer')
@login_required   # 渲染分析器主页面
def gotoanalyzer():
    # print(session['username'] + " has login to analyzer")
    return render_template('analyzer.html',
                           logged_user="当前登录用户: " + session['username'],
                           title_by_user=session['username'] + "的分析器")


@app.route('/ready')
def readyforuse():
    if collect_users.find_one({'username': 'Admin'}):
        return render_template('login.html', errormsg='请直接登录!')
    else:
        return render_template('readyforuse.html')


@app.route('/admin')
@login_required
def adminconsole():
    return render_template('adminconsole.html')

# --------------------------------------------------
# 视图函数_API


@app.route('/firstreg', methods=['POST', 'GET'])  # 首次使用时使用
def firstreg():
    if collect_users.find_one({'username': 'Admin'}):
        abort(404)
    else:
        jsonstr = request.data
        jsonDict = json.loads(jsonstr, encoding='utf-8')
        username = jsonDict['username']
        if collect_users.find_one({'username': username}):
            return jsonify({'status': 'error', 'msg': '该用户已存在'})
        else:
            passwd = jsonDict['passwd']
            passwd_hash = generate_password_hash(passwd)
            collect_users.insert_one(
                {'username': username, 'passwd_hash': passwd_hash, 'status': 'enable'})
            return jsonify({"status": 'ok'})


@app.route('/register', methods=['POST'])
@login_required
def registeruser():
    jsonstr = request.data
    jsonDict = json.loads(jsonstr, encoding='utf-8')
    username = jsonDict['username']
    if collect_users.find_one({'username': username}):
        return jsonify({'status': 'error', 'msg': '该用户已存在'})
    else:
        passwd = jsonDict['passwd']
        passwd_hash = generate_password_hash(passwd)
        collect_users.insert_one(
            {'username': username, 'passwd_hash': passwd_hash, 'status': 'enable'})
        return jsonify({"status": 'ok'})


@app.route('/alluser', methods=['GET'])
@login_required
def getalluser():
    user = session.get('username')
    if user == 'Admin':
        userquery = list(collect_users.find(
            {'username': {'$ne': 'Admin'}}))  # 排除含Admin的项
        status_map = {'enable': '启用', 'disable': '禁用'}
        class_map = {'enable': 'success', 'disable': 'warning'}
        result = [admin_table_templates % (class_map[uinfo['status']],
                                           uinfo['username'],
                                           status_map[uinfo['status']],
                                           uinfo['username']) for uinfo in userquery]
        return jsonify({'status': 'ok', 'result': result})
    else:
        abort(404)


@app.route('/manipulate', methods=['POST'])
@login_required
def user_manipulate():
    user = session.get('username')
    if user == 'Admin':
        jsonstr = request.data
        jsonDict = json.loads(jsonstr, encoding='utf-8')
        # {"ops":OPS,"users":users}
        if jsonDict['ops'] == 'delete':
            for user in jsonDict['users']:
                collect_users.delete_one({'username': user})
            return jsonify({'status': 'ok'})
        elif jsonDict['ops'] == 'enable':
            query = list(collect_users.find({'status': 'disable'}))
            for user in query:
                if user['username'] in jsonDict['users']:
                    collect_users.update_one({'username': user['username']}, {
                                             '$set': {'status': 'enable'}})
            return jsonify({'status': 'ok'})
        elif jsonDict['ops'] == 'disable':
            query = list(collect_users.find({'status': 'enable'}))
            for user in query:
                if user['username'] in jsonDict['users']:
                    collect_users.update_one({'username': user['username']}, {
                                             '$set': {'status': 'disable'}})
            return jsonify({'status': 'ok'})
        elif jsonDict['ops'] == 'reset':
            # {"ops":OPS,"username":NAME,"passwd":PASSWD}
            username = jsonDict['username']
            querydict = collect_users.find_one({'username': username})
            if querydict:
                newpasswd = jsonDict['passwd']
                psh = generate_password_hash(newpasswd)
                collect_users.update_one({'username': username}, {
                                         '$set': {'passwd_hash': psh}})
                return jsonify({'status': 'ok'})
            else:
                return jsonify({'status': 'error', 'msg': '错误！用户不存在'})
    else:
        abort(404)


@app.route('/validation', methods=['POST'])  # 限定POST方法
def login():      # 验证用户密码
    jsonstr = request.data
    if validation(jsonstr):
        tmp_username = fetch_username(jsonstr)
        curr_user = User()
        curr_user.id = tmp_username
        login_user(curr_user)
        session['username'] = tmp_username
        if tmp_username == 'Admin':  # 如果是admin用户就跳转到Admin页面
            return jsonify({'status': 'Admin'})
        else:
            return jsonify({"status": 'ok'})
    else:
        return jsonify({"status": "error"})


@app.route('/logout', methods=['POST'])
@login_required     # 触发注销
def thelogout():
    jsonstr = request.data
    jsonDict = json.loads(jsonstr, encoding='utf-8')
    if jsonDict['msg'] == "logout":
        # print(session['username'] + " has logout")
        session.pop('username', None)
        logout_user()
        return jsonify({"status": "ok"})


@app.route('/stock_info', methods=['POST'])  # 页面发送数据需要用POST(目前只考虑了一条数据)
@login_required  # 更新股票数据
def return_stockinfo():
    if request.data is not None:
        jsonstr = request.data
        jsonDict = json.loads(jsonstr, encoding='utf-8')
        if jsonDict['msg'] == 'query':
            username = session.get('username')  # 识别用户
            # print(username)
            # print(jsonDict)
            # print({'data': html_templates % tuple(datas[username]), 'status': 'ok'})
            try:
                data = datas[username]
            except Exception:
                data = datas['adminroot']
            return jsonify(
                {'data': html_templates % tuple(data), 'status': 'ok'})
        else:
            return jsonify({'status': 'error'})
    else:
        return jsonify({'status': 'error'})


@app.route('/search', methods=['POST'])  # 页面搜索与读写
@login_required
def search_stock():
    # 处理输入
    jsonstr = request.data  # {stock_name:xxx,industry:xxx}
    jsonDict = json.loads(jsonstr, encoding='utf-8')

    # 分步查询股票数据并构造数据
    industry_url = searchIndustry(jsonDict['industry'])
    if 'error' in industry_url:
        return jsonify(industry_url)  # 直接返回错误信息
    stock_url = searchNEWSURL(industry_url, jsonDict['stock_name'])
    if 'error' in stock_url:
        return jsonify(stock_url)
    result = genData(stock_url)

    session_name = session.get('username')
    result['user'] = [session_name]

    # 检查数据库是否已经有对应股票的链接和数据,处理多用户同时持仓一只股票和已记录股票的冲突
    stock_number = result['stock_id']

    query_result = list(collect_stocks.find({'stock_id': stock_number}))
    if len(query_result) == 0:  # 记录为空，即数据库没有该股票的信息的对应处理方式
        collect_stocks.insert_one(result)
        collect_stock_url.insert_one(
            {'stock_id': stock_number, 'url': result['url']})
        return jsonify({'ok': 'Done'})
    # 不同用户同时持有同一只股票时,持有股票的用户会在这里有名字，而不是在用户信息那里标记持仓
    elif session_name not in query_result[0]['user']:
        new_users = query_result[0]['user']
        new_users.append(session_name)
        collect_stocks.update_one({'stock_id': stock_number}, {
                                  '$set': {'user': new_users}})
        return jsonify({'ok': 'Done'})
    else:  # 用户不可以重复记录股票
        return jsonify({'error': '此股票已经被记录'})
    # return jsonify({'error': '查询错误，请联系管理人员'})  # 爬取失败


@app.route('/statistics_res', methods=['GET'])
@login_required  # 更新统计汇总数据
def return_statistics():
    username = session.get('username')
    if username in statistics.keys():
        return jsonify(statistics[username])
    else:
        return jsonify({"status": 'error'})


# ---------------------------------------------
# 异常状态处理


@app.errorhandler(404)  # 处理404的问题
def not_found(e):
    return render_template("page_not_found.html")


@app.errorhandler(405)  # 处理不允许的HTTP谓词
def ban_method(e):
    return render_template("you_cant_use_this_method.html")


if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)
